<script lang="tsx">
import { ref, unref, computed, watch, defineComponent } from 'vue'
import { set, get, cloneDeep } from 'lodash-es'
import { isFunction } from '@/utils/is'
import { humpToDash } from '@/utils'
import { componentMap } from '@/components/Form/src/helper/componentMap'
import { useRenderSelect } from '@/components/Form/src/components/useRenderSelect'
import { useRenderRadio } from '@/components/Form/src/components/useRenderRadio'
import { useRenderCheckbox } from '@/components/Form/src/components/useRenderCheckbox'
import { VueDraggable } from 'vue-draggable-plus'
import { ElFormItem, ElForm, ElCol, ElRow } from 'element-plus'
import {
  FormSchema,
  ComponentNameEnum,
  SelectComponentProps,
  RadioGroupComponentProps,
  CheckboxGroupComponentProps
} from '@/components/Form/src/types/index'

import { setComponentProps } from '@/components/Form/src/helper/index'

const { renderSelectOptions } = useRenderSelect()
const { renderRadioOptions } = useRenderRadio()
const { renderCheckboxOptions } = useRenderCheckbox()

export default defineComponent({
  setup() {
    const scheme = ref([])
    const formModel = ref({})
    const initModel = (schema: FormSchema[], formModel: Recordable) => {
      const model: Recordable = { ...formModel }
      schema.map((v) => {
        if (v.remove) {
          delete model[v.field]
        } else if (v.component !== 'Divider') {
          // const hasField = Reflect.has(model, v.field)
          const hasField = get(model, v.field)
          // 如果先前已经有值存在，则不进行重新赋值，而是采用现有的值
          set(
            model,
            v.field,
            hasField !== void 0 ? get(model, v.field) : v.value !== void 0 ? v.value : undefined
          )
          // model[v.field] = hasField ? model[v.field] : v.value !== void 0 ? v.value : undefined
        }
      })
      // 如果 schema 对应的 field 不存在，则删除 model 中的对应的 field
      for (let i = 0; i < schema.length; i++) {
        const key = schema[i].field
        if (!get(model, key)) {
          delete model[key]
        }
      }
      return model
    }

    // 监听表单结构化数组，重新生成formModel
    watch(
      scheme,
      (schema = []) => {
        formModel.value = initModel(schema, unref(formModel))
      },
      {
        immediate: true,
        deep: true
      }
    )

    const setItemComponentSlots = (slotsProps: Recordable = {}): Recordable => {
      const slotObj: Recordable = {}
      for (const key in slotsProps) {
        if (slotsProps[key]) {
          if (isFunction(slotsProps[key])) {
            slotObj[humpToDash(key)] = (...args: any[]) => {
              return slotsProps[key]?.(...args)
            }
          } else {
            slotObj[humpToDash(key)] = () => {
              return slotsProps[key]
            }
          }
        }
      }
      return slotObj
    }
    // 渲染formItem
    const renderFormItem = (item: FormSchema) => {
      // 如果有optionApi，优先使用optionApi, 并且options不存在或者为空数组
      // if (
      //   item.optionApi &&
      //   (!item.componentProps?.options || !item.componentProps?.options.length)
      // ) {
      //   // 内部自动调用接口，不影响其它渲染
      //   getOptions(item.optionApi, item)
      // }
      const formItemSlots: Recordable = {
        default: () => {
          if (item?.formItemProps?.slots?.default) {
            return item?.formItemProps?.slots?.default(formModel.value)
          } else {
            const Com = componentMap[item.component as string] as ReturnType<typeof defineComponent>

            // const { autoSetPlaceholder } = unref(getProps)

            const componentSlots = (item?.componentProps as any)?.slots || {}
            const slotsMap: Recordable = {
              ...setItemComponentSlots(componentSlots)
            }
            // // 如果是select组件，并且没有自定义模板，自动渲染options
            if (item.component === ComponentNameEnum.SELECT) {
              slotsMap.default = !componentSlots.default
                ? () => renderSelectOptions(item)
                : () => {
                    return componentSlots.default(
                      unref((item?.componentProps as SelectComponentProps)?.options)
                    )
                  }
            }

            // 虚拟列表
            if (item.component === ComponentNameEnum.SELECT_V2 && componentSlots.default) {
              slotsMap.default = ({ item }) => {
                return componentSlots.default(item)
              }
            }

            // 单选框组和按钮样式
            if (
              item.component === ComponentNameEnum.RADIO_GROUP ||
              item.component === ComponentNameEnum.RADIO_BUTTON
            ) {
              slotsMap.default = !componentSlots.default
                ? () => renderRadioOptions(item)
                : () => {
                    return componentSlots.default(
                      unref((item?.componentProps as CheckboxGroupComponentProps)?.options)
                    )
                  }
            }

            // 多选框组和按钮样式
            if (
              item.component === ComponentNameEnum.CHECKBOX_GROUP ||
              item.component === ComponentNameEnum.CHECKBOX_BUTTON
            ) {
              slotsMap.default = !componentSlots.default
                ? () => renderCheckboxOptions(item)
                : () => {
                    return componentSlots.default(
                      unref((item?.componentProps as RadioGroupComponentProps)?.options)
                    )
                  }
            }

            const Comp = () => {
              // 如果field是多层路径，需要转换成对象
              const itemVal = computed({
                get: () => {
                  return get(formModel.value, item.field)
                },
                set: (val) => {
                  set(formModel.value, item.field, val)
                }
              })

              return item.component === ComponentNameEnum.UPLOAD ? (
                <Com
                  vModel:file-list={itemVal.value}
                  {...setComponentProps(item)}
                  style={
                    item.componentProps?.style || {
                      width: '100%'
                    }
                  }
                >
                  {{ ...slotsMap }}
                </Com>
              ) : (
                <Com
                  vModel={itemVal.value}
                  {...setComponentProps(item)}
                  style={
                    item.componentProps?.style || {
                      width: '100%'
                    }
                  }
                >
                  {{ ...slotsMap }}
                </Com>
              )
            }

            return <>{Comp()}</>
          }
        }
      }
      if (item?.formItemProps?.slots?.label) {
        formItemSlots.label = (...args: any[]) => {
          return (item?.formItemProps?.slots as any)?.label(...args)
        }
      }
      if (item?.formItemProps?.slots?.error) {
        formItemSlots.error = (...args: any[]) => {
          return (item?.formItemProps?.slots as any)?.error(...args)
        }
      }
      return (
        <ElFormItem
          v-show={!item.hidden}
          {...(item.formItemProps || {})}
          prop={item.field}
          label={item.label || ''}
        >
          {formItemSlots}
        </ElFormItem>
      )
    }

    return () => (
      <div class="drag-content">
        <ElForm
          class="h100%"
          model={formModel}
          // @ts-ignore
          onSubmit={(e: Event) => {
            e.preventDefault()
          }}
        >
          <ElRow gutter={20} class="h100%">
            <VueDraggable
              class="w100%"
              group="comp"
              v-model={scheme.value}
              modelValue={scheme.value}
            >
              {scheme.value.map((item, index) => (
                <ElCol span={24} key={'scheme' + index}>
                  {renderFormItem(item)}
                </ElCol>
              ))}
            </VueDraggable>
          </ElRow>
        </ElForm>
      </div>
    )
  }
})
</script>

<style lang="less" scoped>
.drag-content {
  height: 100%;
  overflow: hidden;
  padding: 0px 16px;
  border-left: 1px solid #f1f1f1;
  border-right: 1px solid #f1f1f1;
  > div {
    height: 100%;
    width: 100%;
    .comp-item {
      cursor: grab;
    }
  }
  .comp-ghost {
    opacity: 0;
  }
  :deep(.el-scrollbar__view) {
    width: 100%;
    height: 100%;
    > div {
      width: 100%;
      height: 100%;
    }
  }
}
</style>
